package ch13
{
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.geom.Point;
	import flash.utils.setTimeout;

	import utils.Segment;
	import utils.SimpleSlider;

	/**
	 * 让物体真正走起来
	 * @author sjp
	 *
	 */
	public class RealWalk extends Sprite
	{
		private var segment0:Segment;
		private var segment1:Segment;
		private var segment2:Segment;
		private var segment3:Segment;
		private var speedSlider:SimpleSlider;
		private var thighRangeSlider:SimpleSlider;
		private var thighBaseSlider:SimpleSlider;
		private var calfRangeSlider:SimpleSlider;
		private var calfOffsetSlider:SimpleSlider;
		private var gravitySlider:SimpleSlider;
		private var cycle:Number = 0;
		private var vx:Number = 0;
		private var vy:Number = 0;

		public function RealWalk()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			setTimeout(init, 500);
		}

		private function init():void
		{
			segment0 = new Segment(50, 15);
			addChild(segment0);
			segment0.x = 200;
			segment0.y = 100;

			segment1 = new Segment(50, 10);
			addChild(segment1);
			segment1.x = segment0.getPin().x;
			segment1.y = segment0.getPin().y;

			segment2 = new Segment(50, 15);
			addChild(segment2);
			segment2.x = 200;
			segment2.y = 100;

			segment3 = new Segment(50, 10);
			addChild(segment3);
			segment3.x = segment2.getPin().x;
			segment3.y = segment2.getPin().y;

			//速度滑块
			speedSlider = new SimpleSlider(0, 0.3, 0.12);
			addChild(speedSlider);
			speedSlider.x = 10;
			speedSlider.y = 10;

			//大腿运动范围滑块
			thighRangeSlider = new SimpleSlider(0, 90, 45);
			addChild(thighRangeSlider);
			thighRangeSlider.x = 30;
			thighRangeSlider.y = 10;

			//大腿固定端滑块
			thighBaseSlider = new SimpleSlider(0, 180, 90);
			addChild(thighBaseSlider);
			thighBaseSlider.x = 50;
			thighBaseSlider.y = 10;

			//小腿运动范围滑块]
			calfRangeSlider = new SimpleSlider(0, 90, 45);
			addChild(calfRangeSlider);
			calfRangeSlider.x = 70;
			calfRangeSlider.y = 10;

			//小腿偏移滑块
			calfOffsetSlider = new SimpleSlider(-3.14, 3.14, -1.57);
			addChild(calfOffsetSlider);
			calfOffsetSlider.x = 90;
			calfOffsetSlider.y = 10;

			//控制重力
			gravitySlider = new SimpleSlider(0, 1, 0.2);
			addChild(gravitySlider);
			gravitySlider.x = 110;
			gravitySlider.y = 10;

			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}

		private function onEnterFrame(event:Event):void
		{
			doVelocity();
			walk(segment0, segment1, cycle);
			walk(segment2, segment3, cycle + Math.PI);
			cycle += speedSlider.value;
			checkFloor(segment1);
			checkFloor(segment3);
			checkWalls();
		}

		private function walk(segA:Segment, segB:Segment, cyc:Number):void
		{
			var foot:Point = segB.getPin();
			var angleA:Number = Math.sin(cyc) * thighRangeSlider.value + thighBaseSlider.value;
			var angleB:Number = Math.sin(cyc + calfOffsetSlider.value) * calfRangeSlider.value + calfRangeSlider.value;
			segA.rotation = angleA;
			segB.rotation = segA.rotation + angleB;
			segB.x = segA.getPin().x;
			segB.y = segA.getPin().y;
			/**
			 * 处理反作用力
			 * 现在我们已经成功地让双腿与地面发生了接触，但只重置位置还不够真实。因为，行走应该使水平方向也产生运动——x 速度。
			 * 此外，我们的行走应该对 y 速度产生一定的影响——至少要能与重力暂时地抵消一小会儿。在奔跑中，地面之上停留一会儿。
			 * 当脚落地并与地面接触时，就再不能向下了，以便使垂直动量反作用于身体，使身体向上运动。
			 * 脚下落的力气越大，那么被抬起来的力量也就越大。同理，如果在与地面接触时，脚是向后运动的，那么身体就会受到水平的动量，向前进。
			 * 脚向后运动得越快，水平的推力就越大。
			 *
			 * 计算方法:
			 * 如果在这个关节运动之前知道它的枢轴的位置，以及运动后关节的位置，可以求出两者的差值来获得脚的 x,y 速度。
			 *
			 * 每个底部的关节都有各自的 vx,vy 属性，表示底部枢轴点的速度，或虚拟脚的速度，而不是该关节的速度。
			 * */
			segB.vx = segB.getPin().x - foot.x;
			segB.vy = segB.getPin().y - foot.y;
		}

		/**
		 * 移动逻辑
		 */
		private function doVelocity():void
		{
			//重力
			vy += gravitySlider.value;
			segment0.x += vx;
			segment0.y += vy;
			segment2.x += vx;
			segment2.y += vy;
		}

		/**
		 * 底部边界逻辑，脚接触地面的逻辑
		 * @param seg
		 *
		 */
		private function checkFloor(seg:Segment):void
		{
			//脚若超出边界，就将整体位置移动到脚接触地面的位置
			var yMax:Number = seg.getBounds(this).bottom;
			if (yMax > stage.stageHeight)
			{
				var dy:Number = yMax - stage.stageHeight;
				segment0.y -= dy;
				segment1.y -= dy;
				segment2.y -= dy;
				segment3.y -= dy;
				/**
				 * 每个底部的关节都有各自的 vx,vy 属性，表示底部枢轴点的速度，或虚拟脚的速度，而不是该关节的速度。
				 * 当与地面接触时，将它从总速度中减去。
				 * 换句话讲，如果脚在接触地面后，又以每帧 3 像素（vy = 3）运动，我们就要将全部的 vy 将去 3。同样 vx 也是如此。
				 * */
				vx -= seg.vx;
				vy -= seg.vy;
			}
		}

		/**
		 * 左右边界逻辑，屏幕环绕
		 *
		 */
		private function checkWalls():void
		{
			var w:Number = stage.stageWidth + 200;
			//超出右边，移动到左边
			if (segment0.x > stage.stageWidth + 100)
			{
				segment0.x -= w;
				segment1.x -= w;
				segment2.x -= w;
				segment3.x -= w;
			}
			//超出左边，移动到右边
			else if (segment0.x < -100)
			{
				segment0.x += w;
				segment1.x += w;
				segment2.x += w;
				segment3.x += w;
			}
		}
	}
}
